using System;
using GPUAnimationLib.Runtime;
using Unity.Collections;
using Unity.Mathematics;
using UnityEngine;

namespace GPUAnimationLib.Baker
{
    /// <summary>
    /// 骨骼变换矩阵烘焙
    /// </summary>
    public static class BoneMatricesBakerApi
    {
        /// <summary>
        /// 烘焙骨骼变换矩阵到贴图中
        /// </summary>
        /// <param name="skinnedMeshRenderer"></param>
        /// <param name="animator"></param>
        /// <param name="bakerData"></param>
        /// <param name="animationDatas"></param>
        /// <param name="totalFrames"></param>
        /// <returns></returns>
        public static Texture2D BakeAnimationMatricesTexture(SkinnedMeshRenderer skinnedMeshRenderer,
            Animator animator, GPUAnimationBaker.Data bakerData, GPUAnimationData[] animationDatas,
            int totalFrames)
        {
            int boneCount = skinnedMeshRenderer.bones.Length;
            int textureWidth = boneCount * 3;

            NativeArray<half4> boneMatricesTextureData =
                new NativeArray<half4>(totalFrames * textureWidth, Allocator.Temp);

            float4x4 invSkinnedMeshRendererLocalToWorld =
                math.inverse(skinnedMeshRenderer.transform.localToWorldMatrix);
            animator.cullingMode = AnimatorCullingMode.AlwaysAnimate;
            GPUAnimationBakerApi.IterateOverAllFramesThroughAnimator(animator, bakerData, animationDatas,
                (currentFrameIndex) =>
                {
                    for (int boneIndex = 0; boneIndex < boneCount; boneIndex++)
                    {
                        //拿到骨骼
                        Transform boneTransform = skinnedMeshRenderer.bones[boneIndex];
                        if (boneTransform != null)
                        {
                            //计算变换矩阵
                            float4x4 matrix = math.mul(
                                math.mul(invSkinnedMeshRendererLocalToWorld, boneTransform.localToWorldMatrix),
                                skinnedMeshRenderer.sharedMesh.bindposes[boneIndex]
                            );
                            Debug.Assert(Mathf.Approximately(matrix[0][3], 0f)
                                         && Mathf.Approximately(matrix[1][3], 0f)
                                         && Mathf.Approximately(matrix[2][3], 0f)
                                         && Mathf.Approximately(matrix[3][3], 1f), "matrix row 4 must be 0,0,0,1");
                            int matrixIndex = (currentFrameIndex * textureWidth) + (boneIndex * 3);
                            for (int i = 0; i < 3; i++)
                            {
                                boneMatricesTextureData[matrixIndex + i] = new half4((half)matrix[0][i],
                                    (half)matrix[1][i], (half)matrix[2][i], (half)matrix[3][i]);
                            }
                        }
                    }
                });

            Texture2D boneMatricesTexture = new Texture2D(textureWidth, totalFrames, TextureFormat.RGBAHalf, false, true);
            //boneMatricesTexture.filterMode = FilterMode.Point; //此处需设置为point
            boneMatricesTexture.filterMode = FilterMode.Bilinear;
            boneMatricesTexture.anisoLevel = 0;
            boneMatricesTexture.SetPixelData(boneMatricesTextureData, 0);
            boneMatricesTexture.Apply(false);
            boneMatricesTextureData.Dispose();
            return boneMatricesTexture;
        }
    }
}